#include "WiFi.h"
#include "esp_camera.h"
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "soc/soc.h"           // Disable brownour problems
#include "soc/rtc_cntl_reg.h"  // Disable brownour problems
#include "driver/rtc_io.h"
#include "camera_pins.h"
#include <PubSubClient.h>
#include "base64.hpp"
#include <libb64/cencode.h>
#include <ArduinoJson.h>

// Wifi Info
const char* ssid = "HAIMIAN";
const char* password = "Xld2072681!!";

// MQTT Info
const char* client_id = "68733f9032771f177b4b99f9_monitor1_0_0_2025080613";
const char* mqtt_server = "99dc6497df.st1.iotda-device.cn-north-4.myhuaweicloud.com";
const int mqtt_port = 1883;
const char* mqtt_user = "68733f9032771f177b4b99f9_monitor1";
const char* mqtt_password = "bca3406e70c401515cfb976a2baca066743aac06895c6f2e24eba0aa76f5b8e5";

// Topic Info
const char* mqtt_TopicName = "$oc/devices/{device_id}/sys/properties/report";

//设置相机帧的分辨率大小
framesize_t resolution_ = FRAMESIZE_QVGA;

//定义延时时间1000=1s
#define SLEEP_DELAY 60000  //延迟60s
#define FILE_PHOTO "/photo.jpg"

WiFiClient mqttClient;
PubSubClient client(mqttClient);

void setup_camera() {
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sccb_sda = SIOD_GPIO_NUM;
  config.pin_sccb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 15000000;
  /* @param frame_size   One of
   *                     - FRAMESIZE_96X96,    // 96x96
   *                     - FRAMESIZE_QQVGA,    // 160x120
   *                     - FRAMESIZE_QCIF,     // 176x144
   *                     - FRAMESIZE_HQVGA,    // 240x176
   *                     - FRAMESIZE_240X240,  // 240x240
   *                     - FRAMESIZE_QVGA,     // 320x240
   *                     - FRAMESIZE_CIF,      // 400x296
   *                     - FRAMESIZE_HVGA,     // 480x320
   *                     - FRAMESIZE_VGA,      // 640x480
   *                     - FRAMESIZE_SVGA,     // 800x600
   *                     - FRAMESIZE_XGA,      // 1024x768
   *                     - FRAMESIZE_HD,       // 1280x720
   *                     - FRAMESIZE_SXGA,     // 1280x1024
   *                     - FRAMESIZE_UXGA,     // 1600x1200
   *                     - FRAMESIZE_FHD,      // 1920x1080
   *                     - FRAMESIZE_P_HD,     //  720x1280
   *                     - FRAMESIZE_P_3MP,    //  864x1536
   *                     - FRAMESIZE_QXGA,     // 2048x1536
   *                     - FRAMESIZE_QHD,      // 2560x1440
   *                     - FRAMESIZE_WQXGA,    // 2560x1600
   *                     - FRAMESIZE_P_FHD,    // 1080x1920
   *                     - FRAMESIZE_QSXGA,    // 2560x1920
   */
  config.frame_size = FRAMESIZE_SVGA;
  config.pixel_format = PIXFORMAT_RGB565;  // for streaming
  config.fb_location = CAMERA_FB_IN_PSRAM;
  config.fb_count = 1;

  // 相机初始化
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  } else {
    Serial.printf("Camera init succeed");
  }
}

// char* getJSON(char* base64str)
// {


// }

//拍照发送到mqtt
void getimg() {  
  // 1. 获取RGB565图像
  camera_fb_t* rgb565_fb = esp_camera_fb_get();
  if (!rgb565_fb) {
    Serial.println("Get RGB565 img failed!\n");
    return;
  }

  // 2. 转换为JPEG
  uint8_t* jpg_buf = NULL;  // 存储转换后的 JPEG 数据
  size_t jpg_len = 0;       // 存储 JPEG 数据长度
  bool success = frame2jpg(rgb565_fb, 50, &jpg_buf, &jpg_len);   // 第二个参数是 JPEG 质量（0-100，数值越大质量越好）
  esp_camera_fb_return(rgb565_fb);  // 释放原始 RGB565 帧缓冲区（转换后不再需要）
  if (!success) {
    Serial.println("RGB565 to JPEG failed\n");
    free(jpg_buf);
    return;
  }
  Serial.printf("frame to jpeg succeed,JPEG length:%d byte\r\n", jpg_len);

  // 3. 计算 Base64 输出缓冲区大小
  size_t base64_buf_size = ((jpg_len + 2) / 3) * 4 + 1;  // +1 确保有空间放 null 终止符
  char* base64_buf = (char*)malloc(base64_buf_size);
  if (!base64_buf) {
    Serial.println("Base64 malloc failed!\n");
    free(jpg_buf);  // 释放 JPEG 缓冲区
    return;
  }

  // 4. 使用库函数 encode_base64() 进行编码,函数参数：(输入数据, 输入长度, 输出缓冲区) ,库会自动添加 null 终止符，因此 base64_buf 可直接作为字符串使用
  size_t base64_len = encode_base64(jpg_buf, jpg_len, (unsigned char*)base64_buf);
  Serial.printf("Base64 encode succeed,length: %dbyte\r\n", base64_len);
  
  // 5. 构造MQTT JSON结构
  JsonDocument doc;  
  JsonArray services = doc["services"].to<JsonArray>();
  JsonObject service = services.add<JsonObject>();
  service["service_id"] = "Monitor";  // 设置service_id
  JsonObject properties = service["properties"].to<JsonObject>();
  properties["img"] = base64_buf;    // 传入Base64图像数据
  String jsonString;
  serializeJson(doc, jsonString);

  // 6.直接发送出去
  if (client.publish(mqtt_TopicName, jsonString.c_str())) {
    Serial.printf("send succeed,JPEG lengh: %dbyte,Base64 length: %dbyte\r\n",
                  jpg_len, base64_len);
  } else {
    Serial.println("MQTT send failed\r\n");
  }

  // 7. 释放 JPEG 缓冲区,释放 Base64 缓冲区（必须！）
  free(jpg_buf);
  free(base64_buf);
}

void setup_wifi() {
  delay(10);
  // 连接WIFI
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.print("IP address : ");
  Serial.println(WiFi.localIP());
}

//MQTT重连
void reconnect() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (client.connect(client_id, mqtt_user, mqtt_password)) {
      Serial.println("connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Serial.println();
  // 启用PSRAM（必须在相机初始化前执行）
  if (psramFound()) {
    Serial.printf("PSRAM :%d bytes\r\n", ESP.getPsramSize());  // 应输出8388608字节（8MB）
  } else {
    Serial.println("PSRAM init failed!\r\n");
  }
  setup_camera();                            //设置相机
  setup_wifi();                              //连接WIFI
  client.setServer(mqtt_server, mqtt_port);  //连接MQTT Broker
  client.setKeepAlive(60);                   // 强制设置为60秒，
  client.setBufferSize(65535);
  if (client.connect(client_id, mqtt_user, mqtt_password)) {
    Serial.println("mqtt connected");
  }
}

void loop() {
  getimg();

  if (!client.connected()) {
    reconnect();
  }

  delay(3000);
}
